%%%-------------------------------------------------------------------
%%% @author Administrator
%%% @copyright (C) 2019, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 30. 12月 2019 13:56
%%%-------------------------------------------------------------------
-module(main).
-author("Administrator").

-include("include/myshare.hrl").
-include("include/player.hrl").
-include("include/protocol.hrl").
-include("include/logger.hrl").
-include("include/game.hrl").
-include("include/uri.hrl").

-behaviour(gen_server).

%% API
-export([start_link/0, next_task/0]).

%% gen_server callbacks
-export([init/1,
  handle_call/3,
  handle_cast/2,
  handle_info/2,
  terminate/2,
  code_change/3]).

-define(SERVER, ?MODULE).

-record(state, {}).

%%%===================================================================
%%% API
%%%===================================================================

%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @end
%%--------------------------------------------------------------------
-spec(start_link() ->
  {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
start_link() ->
  gen_server:start_link({local, mainPID}, ?MODULE, [], []).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%% @end
%%--------------------------------------------------------------------
-spec(init(Args :: term()) ->
  {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
  {stop, Reason :: term()} | ignore).
init([]) ->
  %初始化ets
  init_ets(),
  %%TODO
  erlang:send_after(3000, mainPID, {do_init_data_later}), %% 10秒后初始逻辑数据
  {ok, #state{}}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling call messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
    State :: #state{}) ->
  {reply, Reply :: term(), NewState :: #state{}} |
  {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
  {noreply, NewState :: #state{}} |
  {noreply, NewState :: #state{}, timeout() | hibernate} |
  {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
  {stop, Reason :: term(), NewState :: #state{}}).
%%座位号处理
handle_call({index_number, [Players, PlayerId, GameId]}, _From, State) ->
  try
    Reply = game_assist:get_room_index(Players, PlayerId, GameId),
    ?DEBUG("main handle_call reply:~p", [Reply]),
    {reply, Reply, State}
  catch
    Class : Error:Stacktrace ->
      ?ERROR("handle_call index_number class:~p,error:~p,stack:~p", [Class, Error, Stacktrace]),
      {reply, error, State}
  end;

handle_call({add_room_seat, [RoomId, Index, PlayerId]}, From, State) ->
  gen_server:reply(From, ok),
  game_assist:add_room_seat(RoomId, Index, PlayerId),
  {noreply, State};

handle_call({delete_room_seat, [RoomId, PlayerId]}, From, State) ->
  gen_server:reply(From, ok),
  game_assist:delete_room_seat(RoomId, PlayerId),
  {noreply, State};

%%随机数生成
handle_call({random, Number}, _From, State) ->
  ?DEBUG("main handle_call  random number:~p", [Number]),
  try
    RandomNum =
      case Number < 1 of
        true -> 2;
        _ -> sys_util:random(Number)
      end,
    {reply, RandomNum, State}
  catch
    Class:Error:Stacktrace ->
      ?ERROR("handle_call random Number:~p,class:~p,error:~p,stack:~p", [Number, Class, Error, Stacktrace]),
      {reply, error, State}
  end;

handle_call(_Request, _From, State) ->
  {reply, ok, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_cast(Request :: term(), State :: #state{}) ->
  {noreply, NewState :: #state{}} |
  {noreply, NewState :: #state{}, timeout() | hibernate} |
  {stop, Reason :: term(), NewState :: #state{}}).
handle_cast(_Request, State) ->
  {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling all non call/cast messages
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
-spec(handle_info(Info :: timeout() | term(), State :: #state{}) ->
  {noreply, NewState :: #state{}} |
  {noreply, NewState :: #state{}, timeout() | hibernate} |
  {stop, Reason :: term(), NewState :: #state{}}).
%%玩家登录
handle_info({player_login, PlayerRecord2}, State) ->
  player_assist:login(PlayerRecord2),
  {noreply, State};

%%随机取新账号加入当前任务
handle_info({subs, Task_id, Is_look_bet}, State) ->
  case pool_player:rand_player(1) of
    [] ->
      ?INFO("There are no new users!"),
      ok;
    [#player_data{account = Account, password = Password, is_new_player = Is_new_player}] ->
      Task_record = game_assist:get_task_info(Task_id),
      Player_record = #player_info{account = Account, password = Password,
        is_new_player = Is_new_player, task_id = Task_id,
        condition = Task_record#game_task.condition},
      %玩家加入任务表
      Player_list = Task_record#game_task.player_list,
      Task_player = #game_task_player{task_id = Task_id, account = Account, password = Password, is_look_bet = Is_look_bet},
      ets:update_element(?ETS_GAME_TASK, Task_id, {#game_task.player_list, [Task_player | Player_list]}),
      %玩家登陆
      player_assist:login(Player_record)
  end,
  {noreply, State};

handle_info(Msg, State) ->
  try
    case Msg of
      {do_init_data_later} ->
        do_init_data_later();
      {timer_Mins_update} ->
        %公共进程定时任务
        next_task(),
        %检查过期的房间座位和管理员
        check_exp_room(),
        start_Min_update_timer();
      Unkown ->
        ?DEBUG("handle_info:mainRecieve unkown:~p", [Unkown])
    end
  catch
    Class:Error:Stacktrace ->
      ?ERROR("main process Last Message in is:  ~p"
      "Exception hanpened~n[type]:[==========~p==========]~n[what]:~p~n"
      "[stack]:~p~n",
        [Msg, Class, Error, Stacktrace])
  end,
  {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_server terminates
%% with Reason. The return value is ignored.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
    State :: #state{}) -> term()).
terminate(_Reason, _State) ->
  ok.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Convert process state when code is changed
%%
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @end
%%--------------------------------------------------------------------
-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
    Extra :: term()) ->
  {ok, NewState :: #state{}} | {error, Reason :: term()}).
code_change(_OldVsn, State, _Extra) ->
  {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================

init_ets() ->
  %%玩家信息ets表
  ets:new(?ETS_PLAYER_INFO, [set, public, named_table, {keypos, #player_info.account}]),
  %%玩家房间信息表
  ets:new(?ETS_ROOM_INFO, [set, public, named_table, {keypos, #room_info.account}]),
  %%网络协议
  ets:new(?ETS_PLAYER_PROTOCOL, [set, public, named_table, {keypos, #player_protocol.account}]),
  %%房间座位信息
  ets:new(?ETS_ROOM_SEAT, [set, public, named_table, {keypos, #room_seat_info.room_id}]),
  %%排队数据
  ets:new(?ETS_ROBOT_QUEUE, [set, public, named_table, {keypos, #robot_queue.room_id}]),
  %%任务表
  ets:new(?ETS_GAME_TASK, [set, public, named_table, {keypos, #game_task.task_id}]),
  %%玩家进入房间表
  ets:new(?ETS_PLAYER_INTO_ROOM, [set, public, named_table, {keypos, #player_into_room.account}]),
  %%账号池表
  ets:new(?ETS_POOL_PLAYER, [set, public, named_table, {keypos, #player_data.account}]).

do_init_data_later() ->
  %%清空玩家表
  database:init_app(),
  %%加载任务
  client:start_task(),
  %%加载玩家及登录
%%  client:load_player(),
  mainPID ! {timer_Mins_update}.

%%定时器
start_Min_update_timer() ->
  {_, {_Hour, _Minute, Second}} = calendar:now_to_local_time(erlang:timestamp()),
  erlang:send_after((60 - Second) * 1000, self(), {timer_Mins_update}).

%%下一个任务
next_task() ->
  GameTask = ets:tab2list(?ETS_GAME_TASK),
  ?DEBUG("next_task,gameTask:~p", [GameTask]),
  Fun2 = fun(Task) ->
    #game_task{task_id = TaskId, start_at = StartAt, end_at = EndAt, status = TaskState,
      condition = TaskCondition, is_all_day_run = Is_all_day_run} = Task,
    NowTimestamp = sys_util:timestamp(),
    if
      Is_all_day_run =:= false andalso TaskState =:= ?TASK_RUN andalso NowTimestamp > EndAt ->
        game_assist:delete_task(TaskId),
        loop_task(Task),
        database:update_task_status(TaskId, ?TASK_NOT_RUN);
      true -> ok
    end,
    Tname = list_to_atom("task_"++integer_to_list(TaskId)),
    case TaskState =:= ?TASK_NOT_RUN andalso ((NowTimestamp > StartAt andalso NowTimestamp < EndAt)
      orelse Is_all_day_run =:= true) of
      true ->
        PlayerList = get_task_player(TaskId),
        ets:update_element(?ETS_GAME_TASK, TaskId, {#game_task.player_list, PlayerList}),
        case PlayerList of
          [] ->
            ?ERROR("There is no account to run the new task task_id:~p", [TaskId]),
            ok;
          _ ->
            NumList = lists:seq(1, erlang:length(PlayerList)),
            ?INFO("mian num_list :~p,player_list:~p", [NumList, PlayerList]),
            lists:foreach(
              fun(Num2) ->
                #game_task_player{account = Account, password = Password, is_new_player = Is_new_Player} = lists:nth(Num2, PlayerList),
                PlayerRecord = #player_info{account = Account, password = Password, is_new_player = Is_new_Player},
                Random = sys_util:random(4),
                Num3 = (TaskId rem 5) + Random,
                Login_after = Num2 * 3000 + Num3 * 1000 + sys_util:random(3000),
                Login_after_s =
                  case erlang:get(Tname) of
                    Login_after ->
                      Login_after + 2000;
                    undefined ->
                      Login_after;
                    _->Login_after
                  end,
                erlang:put(Tname, Login_after_s),
                PlayerRecord2 = PlayerRecord#player_info{condition = TaskCondition, task_id = TaskId, login_after_s = Login_after_s},
                %开始新任务将上一个玩家踢下线重新登录
                EtsPlayerInfo = player_assist:get_player_info(Account),
                case player_assist:get_player_info(Account) of
                  [] -> ?DEBUG("start login account:~p after_time:~p ", [Account, Login_after_s]),
                    erlang:send_after(Login_after_s, self(), {player_login, PlayerRecord2});
                  EtsPlayerInfo ->
                    PlayerPid = EtsPlayerInfo#player_info.pid,
                    case erlang:is_pid(PlayerPid) of
                      true ->
                        ?DEBUG("restart login account:~p", [Account]),
                        PlayerPid ! {update_task, TaskId};
                      _ ->
                        ?DEBUG("start login account:~p after_time ", [Account, Login_after_s]),
                        erlang:send_after(Login_after_s, self(), {player_login, PlayerRecord2})
                    end
                end
              end,
              NumList),
            %%修改任务状态
            ets:update_element(?ETS_GAME_TASK, TaskId, {#game_task.status, ?TASK_RUN}),
            database:update_task_status(TaskId, ?TASK_RUN),
            ok
        end;
      _ ->
        ?INFO("There are no quests that meet the criteria!"),
        erlang:erase(Tname),
        ok
    end
         end,
  lists:foreach(Fun2, GameTask).


get_task_player(Task_id) ->
  case game_assist:get_task_info(Task_id) of
    [] -> [];
    #game_task{player_number = Player_number, look_number = Look_number} ->
      %取指定数量玩家账号
      PlayerRow = pool_player:rand_player(Player_number + Look_number),
      ?DEBUG("playerrow:~p", [PlayerRow]),
      {_, PlayerList} = lists:foldl(
        fun(#player_data{account = Account, password = Password, is_new_player = Is_new_player}, {Look_num, Acc2}) ->
          TaskPlayer =
            #game_task_player{
              task_id = Task_id,
              account = Account,
              password = Password,
              is_new_player = Is_new_player
            },
          TaskPlayer2 =
            case Look_num < Look_number of
              true -> TaskPlayer#game_task_player{is_look_bet = true};
              _ -> TaskPlayer
            end,

          Look_num2 = Look_num + 1,
          if
            Acc2 == [] ->
              {Look_num2, [TaskPlayer2]};
            true ->
              {Look_num2, [TaskPlayer2 | Acc2]}
          end
        end, {0, []},
        PlayerRow),
      PlayerList
  end.

%% 验证房间是否过期超过5小时删除
check_exp_room() ->
  Five_hours = 60 * 60 * 10,
  Now = sys_util:timestamp(),
  case ets:tab2list(?ETS_ROOM_SEAT) of
    [] -> ok;
    RoomSeat ->
      lists:foreach(
        fun(Seat) ->
          case Now > (Seat#room_seat_info.created_at + Five_hours) of
            true ->
              ?INFO("delete exp room time roomid:~p", [Seat#room_seat_info.room_id]),
              ets:delete(?ETS_ROBOT_QUEUE, Seat#room_seat_info.room_id),
              ets:delete(?ETS_ROOM_SEAT, Seat#room_seat_info.room_id),
              scheduling:delete_room_number(Seat#room_seat_info.room_id);
            _ -> ok
          end
        end, RoomSeat)
  end.

%%次日循环
loop_task(#game_task{task_id = Task_id, start_at = Start_at, end_at = End_at}) ->
  New_start_at = Start_at + ?DAY_TIMESTAMP,
  New_end_at = End_at + ?DAY_TIMESTAMP,
  ets:update_element(?ETS_GAME_TASK, Task_id,
    [{#game_task.start_at, New_start_at}, {#game_task.end_at, New_end_at}, {#game_task.status, ?TASK_NOT_RUN},{ #game_task.player_list, []}]).












